#define _GNU_SORUCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define MAX_EPOLL_EVENT 1024
#define BUFFER_SIZE 1024

int SetNonBlocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

void AddFd(int epollfd, int fd)
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    SetNonBlocking(fd);
}

void Work(int epfd, int clisocket)
{
    epoll_event events[MAX_EPOLL_EVENT];
    int pipefd[2];
    int ret = pipe(pipefd);
    assert(ret != -1);
    bool flag = true;
    while (flag)
    {
        int num = epoll_wait(epfd, events, MAX_EPOLL_EVENT, -1);
        // 先解决客户端接收接盘输入并发送给服务器的问题
        for (int i = 0; i < num; i++)
        {
            if ((events[i].data.fd == 0) & EPOLLIN)
            {
                splice(0, nullptr, pipefd[1], nullptr, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
                splice(pipefd[0], nullptr, clisocket, nullptr, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
            }
            else if ((events[i].data.fd == clisocket) & EPOLLIN)
            {
                char read_buffer[BUFFER_SIZE];
                recv(events[i].data.fd, read_buffer, BUFFER_SIZE - 1, 0);
                printf("%s", read_buffer);
            }
            else if ((events[i].data.fd == clisocket) & EPOLLRDHUP)
            {
                printf("服务器关闭了链接！\n");
                flag = false;
            }
        }
    }
}

/*客户端要给epoll注册两个文件描述符，一个是标准输入的读，一个是链接到服务器的sockfd*/
int main(int argc, char **argv)
{
    if (argc < 2)
    {
        printf("使用：%s ip地址 端口号！\n", basename(argv[0]));
        return 1;
    }
    // 先创建epoll
    int epfd = epoll_create(5);
    // 把标准输入的FD登录进去
    AddFd(epfd, 0);
    // 创建客户端的client
    sockaddr_in addrrss;
    memset(&addrrss, 0, sizeof(addrrss));
    addrrss.sin_family = AF_INET;
    addrrss.sin_port = htons(atoi(argv[2]));
    inet_pton(AF_INET, argv[1], &addrrss.sin_addr);
    int clisocket = socket(AF_INET, SOCK_STREAM, 0);
    // 把客户端的FD也登录进EPOLL中
    AddFd(epfd, clisocket);
    // 客户端connect到服务器
    int ret = connect(clisocket, (sockaddr *)&addrrss, sizeof(addrrss));
    // assert(ret != -1);
    Work(epfd, clisocket);
    close(clisocket);
    close(epfd);
    return 0;
}